package com.stockz.scanner;

import java.text.ParseException;
import java.util.Date;
import java.util.List;

import com.stockz.core.manager.BarManager;
import com.stockz.core.manager.ConfigurationManager;
import com.stockz.core.manager.ScripManager;
import com.stockz.core.model.Bar;
import com.stockz.core.model.Scrip;
import com.stockz.core.util.CoreConstant;
import com.stockz.core.util.Global;
import com.stockz.core.util.SpringUtil;
import com.stockz.downloaderx.DownloadManager;
import com.stockz.downloaderx.util.DownloaderConstant;
import com.stockz.scanner.callback.ScannerCallback;
import com.stockz.scanner.model.ScannerEvent;
import com.stockz.scanner.model.ScannerEvent.EventType;
import com.stockz.scanner.screen.MatchStrengthCalculatingScreen;
import com.stockz.scanner.screen.Screen;

public class Scanner{

	private ScripManager scripManager;
	private BarManager barManager;
	private ConfigurationManager configurationManager;
	private boolean cancelRequested = false;
	private int probingSpan = 1;
	private int offset = 0;
	
	public void cancel() {
		cancelRequested = true;
	}
	
	public Scanner() {
		scripManager = SpringUtil.getApplicationContext().getBean(ScripManager.class);
		barManager = SpringUtil.getApplicationContext().getBean(BarManager.class);
		configurationManager = SpringUtil.getApplicationContext().getBean(ConfigurationManager.class);
	}
	
	
	private void executeCallbacks(List<ScannerCallback> scannerCallbacks, ScannerEvent scannerEvent){
		if(scannerCallbacks != null && !scannerCallbacks.isEmpty()){
			for(ScannerCallback callback : scannerCallbacks){
				callback.onScannerEvent(scannerEvent);
			}
		}
	}
	public boolean isEligible(List<Bar> barList, Screen<?> screen, Date scanDate){
		return barList != null && screen != null && scanDate != null
				&& barList.size() > screen.getMandatorySpan() && !barList.get(0).getDate().before(scanDate);
	}
	
	public List<Scrip> getScannableScrips(){
		return scripManager.getAllScrip(getScanDate());
	}
	
	public Date getScanDate(){
		return (Date)Global.get(CoreConstant.keyLastTradingDate);
	}
	
	public void scan(Screen<? extends Object> screen, 
			List<ScannerCallback> scannerCallbacks) throws Exception{
		DownloadManager.lock();
		if(screen == null || scannerCallbacks == null || scannerCallbacks.isEmpty()){
			return;
		}
		
		List<Scrip> scripList = getScannableScrips();
		ScannerEvent event = new ScannerEvent();
		event.setScreen(screen);
		event.setTotalScripCount(scripList.size());
		event.setEventType(EventType.OnScanStart);
		executeCallbacks(scannerCallbacks, event);
		
		if(scripList != null && !scripList.isEmpty()){
			int totalMatchCount = 0;
			int totalSkippedCount = 0;
			for(int i = 0; i < scripList.size(); i++){
				event.setCurrentScripIndex(i);
				if(cancelRequested){
					cancelRequested = false;
					event.setEventType(EventType.OnScanCancel);
					executeCallbacks(scannerCallbacks, event);
					break;
				}
				
				List<Bar> barList = barManager.getBarList(
						scripList.get(i).getCode(),
						screen.getMandatorySpan()+getProbingSpan()+getOffset()+1);
				
				if(isEligible(barList, screen, getScanDate())){
					totalSkippedCount ++;
					event.setTotalSkippedCount(totalSkippedCount);
					event.setEventType(EventType.OnScripScanSkipped);
					executeCallbacks(scannerCallbacks, event);
					continue;
				}
								
				event.setScrip(scripList.get(i));
				event.setBar(barList.get(0));
				event.setEventType(EventType.OnScripScanStart);
				executeCallbacks(scannerCallbacks, event);
				
				boolean match = false;
				for(int j = 0; j < getProbingSpan(); j++){
					List<Bar> subList = barList.subList(j, barList.size()); 
					if(screen.isMatch(subList.subList(getOffset(),subList.size()))){
						match = true;
						break;
					}
				}
				
				if(match){
					totalMatchCount++;
					event.setEventType(EventType.OnMatch);
					if(screen instanceof MatchStrengthCalculatingScreen){
						MatchStrengthCalculatingScreen<? extends Object> matchStrengthCalculatingScreen = 
							(MatchStrengthCalculatingScreen<? extends Object>)screen;
						event.setMatchStrength(matchStrengthCalculatingScreen.getMatchStrength());
					}
					executeCallbacks(scannerCallbacks, event);
					event.setTotalMatchCount(totalMatchCount);
				}else{
					event.setTotalMatchCount(totalMatchCount);
					event.setEventType(EventType.OnNotMatch);
					executeCallbacks(scannerCallbacks, event);
				}
				
				event.setEventType(EventType.OnScripScanEnd);
				executeCallbacks(scannerCallbacks, event);
				
			}
			
			event.setEventType(EventType.OnScanEnd);
			executeCallbacks(scannerCallbacks, event);
			
		}
		DownloadManager.unlock();
	}

	public int getProbingSpan() {
		return probingSpan;
	}

	public void setProbingSpan(int probingSpan) {
		if(probingSpan < 1){
			throw new IllegalArgumentException("Probing Span can not be less than 1");
		}
		this.probingSpan = probingSpan;
	}

	public int getOffset() {
		return offset;
	}

	public void setOffset(int offset) {
		if(offset < 0){
			throw new IllegalArgumentException("Offset can not be negetive");
		}
		this.offset = offset;
	}
}
